import Axios, {
    AxiosInstance,
    AxiosRequestConfig,
    CustomParamsSerializer
} from "axios";
import {
    PureHttpError,
    RequestMethods,
    PureHttpResponse,
    PureHttpRequestConfig
} from "./types.d";
import { stringify } from "qs";
import NProgress from "../progress";
import { ElMessage } from 'element-plus'
import { getToken, formatToken } from "@/utils/auth";
import { useUserStoreHook } from "@/store/modules/user";

// 创建一个新的XMLHttpRequest对象
let xhr = new XMLHttpRequest();
// 打开一个GET请求，请求的URL为serverConfig.json，第三个参数为false，表示同步请求
xhr.open('get', 'serverConfig.json', false)
// 发送请求
xhr.send()

// 将响应的文本解析为JSON对象
let res = JSON.parse(xhr.responseText)

// 相关配置请参考：www.axios-js.com/zh-cn/docs/#axios-request-config-1
const defaultConfig: AxiosRequestConfig = {
    baseURL: res.apiurl,
    // 请求超时时间
    timeout: 40000,
    headers: {
        'Accept-Language': "zh-CN",
        'Content-Type': "application/json;charset=UTF-8",
        'X-Version': "1.0",
        'X-Platform': "1",
        'X-Timestamp': new Date().getTime(),
        'X-HTTP-Method-Override': 'DELETE'
    },
    // 数组格式参数序列化（https://github.com/axios/axios/issues/5142）
    paramsSerializer: {
        serialize: stringify as unknown as CustomParamsSerializer
    }
};

class PureHttp {
    constructor() {
        this.httpInterceptorsRequest();
        this.httpInterceptorsResponse();
    }

    /** token过期后，暂存待执行的请求 */
    private static requests = [];

    /** 防止重复刷新token */
    private static isRefreshing = false;

    /** 初始化配置对象 */
    private static initConfig: PureHttpRequestConfig = {};

    /** 保存当前Axios实例对象 */
    private static axiosInstance: AxiosInstance = Axios.create(defaultConfig);

    /** 重连原始请求 */
    private static retryOriginalRequest(config: PureHttpRequestConfig) {
        return new Promise(resolve => {
            PureHttp.requests.push((token: string) => {
                config.headers["Authorization"] = formatToken(token);
                resolve(config);
            });
        });
    }

    /** 请求拦截 */
    private httpInterceptorsRequest(): void {
        PureHttp.axiosInstance.interceptors.request.use(
            async (config: PureHttpRequestConfig): Promise<any> => {
                // 开启进度条动画
                NProgress.start();
                // 优先判断post/get等方法是否传入回调，否则执行初始化设置等回调
                if (typeof config.beforeRequestCallback === "function") {
                    config.beforeRequestCallback(config);
                    return config;
                }
                if (PureHttp.initConfig.beforeRequestCallback) {
                    PureHttp.initConfig.beforeRequestCallback(config);
                    return config;
                }
                /** 请求白名单，放置一些不需要token的接口（通过设置请求白名单，防止token过期后再请求造成的死循环问题） */
                const whiteList = ["/getLogin"];
                return whiteList.some(v => config.url.indexOf(v) > -1)
                    ? config
                    : new Promise(resolve => {
                        if (getToken()) {
                            config.headers['X-Token'] = getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
                        }
                        resolve(config);
                    });
            },
            error => {
                return Promise.reject(error);
            }
        );
    }

    /** 响应拦截 */
    private httpInterceptorsResponse(): void {
        const instance = PureHttp.axiosInstance;
        instance.interceptors.response.use(
            (response: PureHttpResponse) => {
                const $config = response.config;
                // 关闭进度条动画
                NProgress.done();
                // 优先判断post/get等方法是否传入回调，否则执行初始化设置等回调
                if (typeof $config.beforeResponseCallback === "function") {
                    $config.beforeResponseCallback(response);
                    return response.data;
                }
                if (PureHttp.initConfig.beforeResponseCallback) {
                    PureHttp.initConfig.beforeResponseCallback(response);
                    return response.data;
                }

                if (response.data.Code === 0) {
                    return response.data;
                } else if (response.data.Code === 99) {
                    ElMessage({
                        message: response.data.Message,
                        type: 'error'
                    })
                    useUserStoreHook().logOut();
                } else {
                    ElMessage({
                        message: response.data.Message,
                        type: 'error'
                    })
                    return response.data
                }
            },
            (error: PureHttpError) => {
                const $error = error;
                $error.isCancelRequest = Axios.isCancel($error);
                if ($error && $error.response) {
                    // 1.公共错误处理
                    // 2.根据响应码具体处理
                    switch ($error.response.status) {
                        case 400:
                            $error.message = '错误请求(400)'
                            break;
                        case 401:
                            $error.message = '未授权，请重新登录(401)'
                            break;
                        case 403:
                            $error.message = '拒绝访问(403)'
                            break;
                        case 404:
                            $error.message = '请求网址域名错误,未找到该网页资源(404 not found)'
                            // window.location.href = "/error"
                            break;
                        case 405:
                            $error.message = '请求类型未允许(405 Method not allowed)'
                            break;
                        case 406:
                            $error.message = '(请求不接受 406)无法使用请求的内容特性响应请求的网页'
                            break;
                        case 407:
                            $error.message = '该IP服务被禁止(407),请开启代理授权'
                            break;
                        case 408:
                            $error.message = '请求超时(408)'
                            break;
                        case 409:
                            $error.message = '(服务器冲突 409) 服务器在完成请求时发生冲突。服务器必须在响应中包含有关冲突的信息'
                            break;
                        case 410:
                            $error.message = '（服务器资源不存在 410）请求的资源已永久删除'
                            break;
                        case 411:
                            $error.message = '(需要有效长度 411） 服务器不接受不含有效内容长度标头字段的请求'
                            break;
                        case 412:
                            $error.message = '(未满足前提条件 412） 服务器未满足请求者在请求中设置的其中一个前提条件'
                            break;
                        case 413:
                            $error.message = '（请求实体过大 413） 服务器无法处理请求，因为请求实体过大，超出服务器的处理能力'
                            break;
                        case 414:
                            $error.message = '（请求的URI过长 414） 请求的URI过长，服务器无法处理。'
                            break;
                        case 415:
                            $error.message = '（不支持的媒体类型 415） 请求的格式不受请求页面的支持'
                            break;
                        case 416:
                            $error.message = '（请求范围不符合要求 416） 如果页面无法提供请求的范围，则服务器会返回此状态代码'
                            break;
                        case 417:
                            $error.message = '（未满足期望值 417） 服务器未满足”期望”请求标头字段的要求'
                            break;
                        case 500:
                            $error.message = '服务器端出错(500)'
                            break;
                        case 501:
                            $error.message = '服务器不具备完成请求的功能(501)'
                            break;
                        case 502:
                            $error.message = '网络错误,服务器端无响应(502)'
                            break;
                        case 503:
                            $error.message = '服务不可用(503)'
                            break;
                        case 504:
                            $error.message = '网关超时(504)'
                            break;
                        case 505:
                            $error.message = '（HTTP版本不受支持 505)服务器不支持请求中所用的HTTP协议版本'
                            break;
                        default:
                            $error.message = `连接错误${$error.response.status}`
                    }
                }
                // 关闭进度条动画
                NProgress.done();
                // 所有的响应异常 区分来源为取消请求/非取消请求
                return Promise.reject($error);
            }
        );
    }

    /** 通用请求工具函数 */
    public request<T>(
        method: RequestMethods,
        url: string,
        param?: AxiosRequestConfig,
        axiosConfig?: PureHttpRequestConfig
    ): Promise<T> {
        const config = {
            method,
            url,
            ...param,
            ...axiosConfig
        } as PureHttpRequestConfig;
        // 单独处理自定义请求/响应回调
        return new Promise((resolve, reject) => {
            PureHttp.axiosInstance
                .request(config)
                .then((response: undefined) => {
                    resolve(response);
                })
                .catch(error => {
                    reject(error);
                });
        });
    }

    /** 单独抽离的post工具函数 */
    public post<T, P>(
        url: string,
        params?: AxiosRequestConfig<T>,
        config?: PureHttpRequestConfig
    ): Promise<P> {
        return this.request<P>("post", url, params, config);
    }

    /* 单独抽离的get工具函数 */
    public get<T, P>(
        url: string,
        params?: AxiosRequestConfig<T>,
        config?: PureHttpRequestConfig
    ): Promise<P> {
        return this.request<P>("get", url, params, config);
    }
}

export const http = new PureHttp();